﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace Mars.MeshUI
{
    /// <summary>
    /// copy from ugui
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObjectPool<T> where T : new()
    {
        public delegate T CreateObjAction();

        private readonly Stack<T> m_Stack = new Stack<T>();
        private readonly UnityAction<T> m_ActionOnGet;
        private readonly UnityAction<T> m_ActionOnRelease;
        private readonly CreateObjAction m_ActionOnCreate;
        private readonly UnityAction<T> m_ActionOnDestory;

        public int countAll { get; private set; }

        public int countActive
        {
            get { return countAll - countInactive; }
        }

        public int countInactive
        {
            get { return m_Stack.Count; }
        }

        public ObjectPool(UnityAction<T> actionOnGet = null, UnityAction<T> actionOnRelease = null,
            CreateObjAction actionOnCreate = null, UnityAction<T> actionOnDestory = null)
        {
            m_ActionOnGet = actionOnGet;
            m_ActionOnRelease = actionOnRelease;
            m_ActionOnCreate = actionOnCreate;
            m_ActionOnDestory = actionOnDestory;
        }


        public void Clear()
        {
            if (m_Stack.Count == 0)
            {
                return;
            }

            if (null != m_ActionOnDestory)
            {
                T t = m_Stack.Pop();
                for (; null != t;)
                {
                    m_ActionOnDestory(t);
                    if (m_Stack.Count == 0)
                    {
                        break;
                    }

                    t = m_Stack.Pop();
                }
            }
            else
            {
                m_Stack.Clear();
            }
        }

        private T CreateNew()
        {
            T element;
            if (null != m_ActionOnCreate)
            {
                element = m_ActionOnCreate();
            }
            else
            {
                element = new T();
            }

            countAll++;


            return element;
        }

        public T Get()
        {
            T element;
            if (m_Stack.Count == 0)
            {
                element = CreateNew();
                countAll++;
            }
            else
            {
                element = m_Stack.Pop();
            }

            if (m_ActionOnGet != null)
            {
                m_ActionOnGet(element);
            }

            return element;
        }

        public void Release(T element)
        {
            if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
            {
                Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
            }

            if (m_ActionOnRelease != null)
            {
                m_ActionOnRelease(element);
            }

            m_Stack.Push(element);
        }
    }

    /// <summary>
    /// copy from ugui
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static class ListPool<T>
    {
        private static readonly ObjectPool<List<T>> s_ListPool = new ObjectPool<List<T>>(null, l => l.Clear());

        public static void ClearAll()
        {
            ListPool<int>.Clear();
            ListPool<uint>.Clear();
            ListPool<bool>.Clear();
            ListPool<float>.Clear();
            ListPool<Color>.Clear();
            ListPool<Texture>.Clear();
        }

        public static void Clear()
        {
            s_ListPool.Clear();
        }

        public static List<T> Get()
        {
            return s_ListPool.Get();
        }

        public static void Release(List<T> toRelease)
        {
            if (toRelease == null)
            {
                return;
            }

            toRelease.Clear();
            s_ListPool.Release(toRelease);
        }
    }
}